/*
 * src/interfaces/libpq/win32.c
 *
 *
 *	FILE
 *		win32.c
 *
 *	DESCRIPTION
 *		Win32 支持函数。
 *
 * 包含用于查找 win32 套接字错误描述的表和函数。但可能包含其他 win32 辅助函数
 * 用于 libpq。
 *
 * 错误常量来自 Frambak Bakfram LGSOCKET 库的人，他们又从 Winsock FAQ 中获取。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 */

/* 使编译更快，排除未使用的内容 */

#define VC_EXTRALEAN
#ifndef __MINGW32__
#define NOGDI
#endif
#define NOCRYPT

#include "postgres_fe.h"

#include "win32.h"

/* 在这里声明，以避免引入所有包含文件，这会导致名称冲突 */
#ifdef ENABLE_NLS
extern char *libpq_gettext(const char *msgid) pg_attribute_format_arg(1);
#else
#define libpq_gettext(x) (x)
#endif


static struct WSErrorEntry
{
	DWORD		error;
	const char *description;
}			WSErrors[] =

{
	{
		0, "No error"
	},
	{
		WSAEINTR, "Interrupted system call"
	},
	{
		WSAEBADF, "Bad file number"
	},
	{
		WSAEACCES, "Permission denied"
	},
	{
		WSAEFAULT, "Bad address"
	},
	{
		WSAEINVAL, "Invalid argument"
	},
	{
		WSAEMFILE, "Too many open sockets"
	},
	{
		WSAEWOULDBLOCK, "Operation would block"
	},
	{
		WSAEINPROGRESS, "Operation now in progress"
	},
	{
		WSAEALREADY, "Operation already in progress"
	},
	{
		WSAENOTSOCK, "Socket operation on non-socket"
	},
	{
		WSAEDESTADDRREQ, "Destination address required"
	},
	{
		WSAEMSGSIZE, "Message too long"
	},
	{
		WSAEPROTOTYPE, "Protocol wrong type for socket"
	},
	{
		WSAENOPROTOOPT, "Bad protocol option"
	},
	{
		WSAEPROTONOSUPPORT, "Protocol not supported"
	},
	{
		WSAESOCKTNOSUPPORT, "Socket type not supported"
	},
	{
		WSAEOPNOTSUPP, "Operation not supported on socket"
	},
	{
		WSAEPFNOSUPPORT, "Protocol family not supported"
	},
	{
		WSAEAFNOSUPPORT, "Address family not supported"
	},
	{
		WSAEADDRINUSE, "Address already in use"
	},
	{
		WSAEADDRNOTAVAIL, "Cannot assign requested address"
	},
	{
		WSAENETDOWN, "Network is down"
	},
	{
		WSAENETUNREACH, "Network is unreachable"
	},
	{
		WSAENETRESET, "Net connection reset"
	},
	{
		WSAECONNABORTED, "Software caused connection abort"
	},
	{
		WSAECONNRESET, "Connection reset by peer"
	},
	{
		WSAENOBUFS, "No buffer space available"
	},
	{
		WSAEISCONN, "Socket is already connected"
	},
	{
		WSAENOTCONN, "Socket is not connected"
	},
	{
		WSAESHUTDOWN, "Cannot send after socket shutdown"
	},
	{
		WSAETOOMANYREFS, "Too many references, cannot splice"
	},
	{
		WSAETIMEDOUT, "Connection timed out"
	},
	{
		WSAECONNREFUSED, "Connection refused"
	},
	{
		WSAELOOP, "Too many levels of symbolic links"
	},
	{
		WSAENAMETOOLONG, "File name too long"
	},
	{
		WSAEHOSTDOWN, "Host is down"
	},
	{
		WSAEHOSTUNREACH, "No route to host"
	},
	{
		WSAENOTEMPTY, "Directory not empty"
	},
	{
		WSAEPROCLIM, "Too many processes"
	},
	{
		WSAEUSERS, "Too many users"
	},
	{
		WSAEDQUOT, "Disc quota exceeded"
	},
	{
		WSAESTALE, "Stale NFS file handle"
	},
	{
		WSAEREMOTE, "Too many levels of remote in path"
	},
	{
		WSASYSNOTREADY, "Network system is unavailable"
	},
	{
		WSAVERNOTSUPPORTED, "Winsock version out of range"
	},
	{
		WSANOTINITIALISED, "WSAStartup not yet called"
	},
	{
		WSAEDISCON, "Graceful shutdown in progress"
	},
	{
		WSAHOST_NOT_FOUND, "Host not found"
	},
	{
		WSATRY_AGAIN, "NA Host not found / SERVFAIL"
	},
	{
		WSANO_RECOVERY, "Non recoverable FORMERR||REFUSED||NOTIMP"
	},
	{
		WSANO_DATA, "No host data of that type was found"
	},
	{
		0, 0
	}							/* 表的结束 */
};


/*
 * 如果未找到返回0，线性查找但谁在乎，此刻
 * 我们已经很痛苦了 :)
 */

static int fc_LookupWSErrorMessage(DWORD fc_err, char *fc_dest)
{
	struct WSErrorEntry *fc_e;

	for (fc_e = WSErrors; fc_e->description; fc_e++)
	{
		if (fc_e->error == fc_err)
		{
			strcpy(fc_dest, fc_e->description);
			return 1;
		}
	}
	return 0;
}


struct MessageDLL
{
	const char *dll_name;
	void	   *handle;
	int			loaded;			/* 布尔值 */
}			dlls[] =

{
	{
		"netmsg.dll", 0, 0
	},
	{
		"winsock.dll", 0, 0
	},
	{
		"ws2_32.dll", 0, 0
	},
	{
		"wsock32n.dll", 0, 0
	},
	{
		"mswsock.dll", 0, 0
	},
	{
		"ws2help.dll", 0, 0
	},
	{
		"ws2thk.dll", 0, 0
	},
	{
		0, 0, 1
	}							/* 最后一个，无dll，总是加载 */
};

#define DLLS_SIZE (sizeof(dlls)/sizeof(struct MessageDLL))

/*
 * 通过首先尝试在查找表中找到套接字错误的描述，如果失败，则尝试
 * 加载任何winsock dll以找到该消息。
 * DLL的功能从Nt4 (spX ?)开始，但某些特殊
 * 版本的winsock可能也具有此功能（在Win98 SE
 * 特殊安装中看到过）			   / Magnus Naeslund (mag@fbab.net)
 *
 */

const char * winsock_strerror(int fc_err, char *fc_strerrbuf, size_t fc_buflen)
{
	unsigned long fc_flags;
	int			fc_offs,
				fc_i;
	int			fc_success = LookupWSErrorMessage(fc_err, fc_strerrbuf);

	for (fc_i = 0; !fc_success && fc_i < DLLS_SIZE; fc_i++)
	{

		if (!dlls[fc_i].loaded)
		{
			dlls[fc_i].loaded = 1; /* 只加载一次 */
			dlls[fc_i].handle = (void *) LoadLibraryEx(dlls[fc_i].dll_name,
													0,
													LOAD_LIBRARY_AS_DATAFILE);
		}

		if (dlls[fc_i].dll_name && !dlls[fc_i].handle)
			continue;			/* 没有加载 */

		fc_flags = FORMAT_MESSAGE_FROM_SYSTEM
			| FORMAT_MESSAGE_IGNORE_INSERTS
			| (dlls[fc_i].handle ? FORMAT_MESSAGE_FROM_HMODULE : 0);

		fc_success = 0 != FormatMessage(fc_flags,
									 dlls[fc_i].handle, fc_err,
									 MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
									 fc_strerrbuf, fc_buflen - 64,
									 0);
	}

	if (!fc_success)
		sprintf(fc_strerrbuf, libpq_gettext("unrecognized socket error: 0x%08X/%d"), fc_err, fc_err);
	else
	{
		fc_strerrbuf[fc_buflen - 1] = '\0';
		fc_offs = strlen(fc_strerrbuf);
		if (fc_offs > (int) fc_buflen - 64)
			fc_offs = fc_buflen - 64;
		sprintf(fc_strerrbuf + fc_offs, " (0x%08X/%d)", fc_err, fc_err);
	}
	return fc_strerrbuf;
}
